package 线程通讯;

/**
 * 【专题】多线程之间的通讯 [可以通过 生产者和消费者共享同一个全局变量的模型 理解]
 * <p>
 * [一]为什么要线程通信？
 * 1. 多个线程并发执行时, 在默认情况下CPU是随机切换线程的, 当我们需要多个线程来共同完成一件任务
 * 并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信, 以此来帮我们达到多线程共同操作一份数据
 * 2.当然如果我们没有使用线程通信来使用多线程共同操作同一份数据的话, 虽然可以实现
 * 但是在很大程度会造成多线程之间对同一共享变量的争夺, 那样的话势必为造成很多错误和损失！
 * 3.所以, 我们才引出了线程之间的通信, 多线程之间的通信能够避免对同一共享变量的争夺
 * <p>
 * [二]什么是线程通信？
 * 多个线程在处理同一个资源, 并且任务不同时, 需要线程通信来帮助解决线程之间对同一个变量的使用或操作, 避免对同一共享变量的[争夺]
 * <p>
 * [三]
 * 1.wait()休眠、notify()唤醒、notifyAll()唤醒全部是三个定义在Object类里的方法, 可以用来控制线程的状态
 * 2.一定要在synchronized同步中使用, 并且使用同一把锁
 * 3.wait()与sleep()区别(wait会释放锁资源, sleep不会释放锁的)
 * <p>
 * Created by huangyu on 2018/8/30 23:01
 */
class Res {
	public String userSex;
	public String userName;
	public Boolean flag = false;// 线程通讯标识
}

class Producer extends Thread {
	private Res res;

	public Producer(Res res) {
		this.res = res;
	}

	@Override
	public void run() {
		int count = 0;
		while (true) {
			synchronized (res) {
				if (!res.flag) {
					try {
						res.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

				// flag为true时生产者进行写
				if (count == 0) {
					res.userName = "黄宇";
					res.userSex = "男";
				} else {
					res.userName = "小红";
					res.userSex = "女";
				}
				count = (count + 1) % 2;
				// 生产者写完过后,notify方法通知给消费者进行读的操作
				res.flag = false;
				res.notify();
			}

		}
	}
}

class Comsumer extends Thread {
	private Res res;

	public Comsumer(Res res) {
		this.res = res;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (res) {
				if (!res.flag) {
					System.out.println(res.userName + "--" + res.userSex);
					res.notify();
				}
				// flag为true时消费者进行等待
				res.flag = true;
				try {
					res.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}
	}
}

public class Thread01 extends Thread {
	public static void main(String[] args) {
		Res res = new Res();
		Producer producer = new Producer(res);
		Comsumer comsumer = new Comsumer(res);
		producer.start();
		comsumer.start();
	}
}
